#include <QMessageBox>
#include <QMutex>
#include <QTime>
#include "libraries/ffmpeg/includes/ffmpeg.h"
#include "libraries/sdl/includes/sdl.h"
#include <iostream>

static void msgbox(QString t, QString s) {
    QMessageBox box;
    box.setText(s);
    box.setWindowTitle(t);
    box.exec();
}

typedef struct PacketQueue
{
    AVPacketList* first;
    AVPacketList* last;
    QMutex* mutex;
    int size;
    static const int MAX_VIDEO_QUEUE_SIZE = 20;
    static const int MAX_AUDIO_QUEUE_SIZE = 20;

    PacketQueue() {
        mutex = new QMutex();
        first = NULL;
        last = NULL;
        size = 0;
    }

    ~PacketQueue() {
        flush();
        size = 0;
        if (!first) {
            last = NULL;
        }
        else {
            if (first == last) {
                av_free_packet(&first->pkt);
                delete first;
                first = NULL;
                last = NULL;
            }
            else {
                AVPacketList* cur = first;
                while (cur) {
                    first = cur;
                    cur = cur->next;
                    av_free_packet(&first->pkt);
                    delete first;
                }
                first = NULL;
                last = NULL;
            }
        }
        delete mutex;
    }

    void push(AVPacket* p) {
        AVPacketList* pkt = new AVPacketList();
        av_dup_packet(p);
        pkt->pkt = *p;
        pkt->next = NULL;

        mutex->lock();

        if (!first)
            first = pkt;
        else
            last->next = pkt;
        last = pkt;
        size++;

        mutex->unlock();
    }
    int pop(AVPacket* p) {
        *p = first->pkt;
        mutex->lock();

        if (!first) {
            return -1;
        }
        else if (first == last) {
            delete first;
            first = NULL;
            last = NULL;
        }
        else {
            AVPacketList* next = first->next;
            //av_free(first);
            delete first;
            first = next;
        }
        size--;

        mutex->unlock();
        return 1;
    }

    void flush() {
        mutex->lock();
        size = 0;
        if (!first) {
            last = NULL;
        }
        else {
            if (first == last) {
                av_free_packet(&first->pkt);
                delete first;
                first = NULL;
                last = NULL;
            }
            else {
                AVPacketList* cur = first;
                while (cur) {
                    first = cur;
                    cur = cur->next;
                    av_free_packet(&first->pkt);
                    delete first;
                }
                first = NULL;
                last = NULL;
            }
        }
        mutex->unlock();
    }
} PacketQueue;

typedef struct PictureQueue {
    typedef struct VideoPicture {
        AVFrame* picture;
        double pts;
        VideoPicture* next;

        VideoPicture(AVFrame* pic, double pts) {
            this->picture = pic;
            this->pts = pts;
            next = NULL;
        }
    } VideoPicture;

    VideoPicture* first;
    VideoPicture* last;
    int size;
    QMutex* mutex;
    static const int MAX_PICTURE_QUEUE_SIZE = 5;

    PictureQueue() {
        size = 0;
        mutex = new QMutex();
        first = NULL;
        last = NULL;
    }
    ~PictureQueue() {
        flush();
        delete mutex;
    }
    int push(AVFrame* pic, double pts) {
        VideoPicture* vp = new VideoPicture(pic, pts);
        if (size == 0) {
            first = vp;
            last = vp;
        }
        else if (vp->pts < first->pts) {
            vp->next = first;
            first = vp;
        }
        else if (vp->pts > first->pts) {
            last->next = vp;
            last = vp;
        }
        else {
            VideoPicture* cur = first->next;
            VideoPicture* prev = first;
            while (vp->pts > cur->pts) {
                prev = cur;
                cur = cur->next;
            }
            vp->next = cur;
            prev->next = vp;
        }
        size++;
        return 1;
    }
    int pop(AVFrame** outPic, double* outPts) {
        if (size == 0)
            return -1;
        *outPic = first->picture;
        *outPts = first->pts;
        if (size == 1) {
            delete first;
            last = NULL;
            first = NULL;
            size = 0;
        }
        else if (size > 1) {
            VideoPicture* next = first->next;
            delete first;
            first = next;
            size--;
        }
        return 1;
    }
    double curpts () {
        if (first)
            return first->pts;
        else
            return 0;
    }
    void flush() {
        mutex->lock();
        VideoPicture* cur = first;
        VideoPicture* next;
        while (cur) {
            av_freep(cur->picture);
            next = cur->next;
            delete cur;
            cur = next;
        }
        first = NULL;
        last = NULL;
        size = 0;
        mutex->unlock();
    }
} PictureQueue;

enum VideoState {PLAY, PAUSED, STOPPED, STOPPING, SEEKING, OTHER};

typedef struct Video
{
    int videoIndex;
    int audioIndex;
    unsigned int volume;
    AVFormatContext* formatCtx;
    AVCodecContext* vCodecCtx;
    AVCodecContext* aCodecCtx;
    AVCodec* vCodec;
    AVCodec* aCodec;
    double aspect_ratio;
    double videoClock;
    double vidTimeElapsed;
    QTime countingTime;
    double decodeClock;

    bool seekFlag;
    double seekPos;

    PacketQueue* vPacketQueue;
    PictureQueue* pQueue;
    PacketQueue* aPacketQueue;


    VideoState state;

    Video() {
        volume = SDL_MIX_MAXVOLUME;
        formatCtx = NULL;
        vCodecCtx = NULL;
        aCodecCtx = NULL;
        vCodec = NULL;
        aCodec = NULL;
        videoIndex = -1;
        audioIndex = -1;
        state = STOPPED;
        seekFlag = false;
        vPacketQueue = new PacketQueue();
        pQueue = new PictureQueue();
        aPacketQueue = new PacketQueue();
        videoClock = 0;
        vidTimeElapsed = 0;
        decodeClock = 0;
    }

    ~Video() {
        // Free packet queues
        if (vPacketQueue)
            delete vPacketQueue;
        if (pQueue)
            delete pQueue;
        if (aPacketQueue)
            delete aPacketQueue;

        // Close the codec
        if (vCodecCtx)
            avcodec_close(vCodecCtx);
        if (aCodecCtx)
            avcodec_close(aCodecCtx);

        // Close the video file
        avformat_close_input(&formatCtx);
    }

    void del(QString m = "") {
        if (m.length() > 0)
            msgbox("Error", m);
        delete this;
    }
} Video;
